use std::{rc::Rc, cell::RefCell, sync::{Arc, Mutex}};

use crate::{instructions::base::Instruction, rtda::{Frame, Thread, Object, ExtraType, heap::string_pool}};


// athrow指令的操作数是一个异常对象引用，从操作数栈弹出。
#[derive(Debug, Default)]
pub struct ATHROW {}

impl Instruction for ATHROW {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let ex = frame.borrow().get_operand_stack().borrow_mut().pop_ref();
        if ex.is_none() {
            panic!("java.lang.NullPointerException");
        }
        let ex = ex.unwrap();
        let thread = frame.borrow().get_thread();
        if !find_and_goto_exception_handler(thread.clone(), ex) {
            handle_uncaught_exception(thread, ex);
        }
    }
}

// 查找并调至异常处理
// 一层层的向上级查找异常处理
fn find_and_goto_exception_handler(thread: Arc<Mutex<Thread>>, ex: *mut Object) -> bool {
    loop {
       let frame = thread.lock().unwrap().current_frame();

       if frame.borrow().get_next_pc() == 0 {
           break;
       }

       let pc = frame.borrow().get_next_pc() - 1;

       let handler_pc = frame.borrow().get_method().find_exception_handler(unsafe { &*ex }.get_class(), pc as i32);
       // 存在异常处理，跳转
       if handler_pc > 0 {
           let stack = frame.borrow().get_operand_stack();
           stack.borrow_mut().clear();
           stack.borrow_mut().push_ref(Some(ex));
           frame.borrow_mut().set_next_pc(handler_pc as usize);
           return true;
       }

       thread.lock().unwrap().pop_frame();

       if thread.lock().unwrap().is_stack_empty() {
           break;
       }
    }
    false
}

// 处理未捕获的异常
fn handle_uncaught_exception(thread: Arc<Mutex<Thread>>, ex: *mut Object) {
    thread.lock().unwrap().clear_stack();

    let j_msg = unsafe { &*ex }.get_ref_var("detailMessage", "Ljava/lang/String;");
    let rs_mag = match j_msg {
        Some(msg) => string_pool::rs_string(msg),
        None => "null".to_string()
    };
    println!("{}: {}", unsafe { &*{ &*ex }.get_class() }.java_name(), rs_mag);

    if let Some(ExtraType::StackTraceElements(stes)) = unsafe { &*ex }.get_extra() {
        for ste in stes {
            println!("\tat {}", ste);
        }
    }

}